#include "acceptor.h"
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "Base/address.h"
#include "channel.h"
#include <errno.h>

using namespace tiny_muduo;

namespace {
    const int kMaxListenNum = 128;
}

Acceptor::Acceptor(EventLoop* loop, const Address& address)
    :loop_(loop),
    listenfd_(::socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)),
    idlefd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)),
    channel_(std::make_unique<Channel>(loop, listenfd_))
{
    // 设置端口复用
    SetSockoptReuseAddr(listenfd_);
    SetSockoptKeepAlive(listenfd_);

    BindListenFd(address);
    channel_->SetReadCallback(std::bind(&Acceptor::NewConnection, this));
}

Acceptor::~Acceptor()
{
    channel_->DisableAll();
    loop_->Remove(channel_.get());
    ::close(listenfd_);
}

// 已经没有用了
int Acceptor::SetNonBlocking(int fd)
{
    int old_state = fcntl(fd, F_GETFL);
    int new_state = old_state | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_state);
    return new_state;
}

void Acceptor::BindListenFd(const Address& addr)
{
    struct sockaddr_in address;
    bzero((char*)&address, sizeof(address));
    // memset(&address, 0, sizeof(address));
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    address.sin_port = htons(addr.port());
    // inet_pton(AF_INET, addr.ip(), &address.sin_addr);

    int ret = bind(listenfd_, (struct sockaddr*)(&address), sizeof(address));
    assert(ret != -1);
    (void)ret; // 消除未使用变量的警告
}

void Acceptor::Listen()
{
    int ret = listen(listenfd_, kMaxListenNum);
    assert(ret != -1);
    (void)ret;
    channel_->EnableReading();
}

void Acceptor::NewConnection()
{
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    int connfd = ::accept4(listenfd_, (struct sockaddr*)&client, &client_addrlength, SOCK_NONBLOCK | SOCK_CLOEXEC);
    if(connfd < 0)
    {
        if(errno == EMFILE) // 超出打开的最大文件描述符
        {
            ::close(idlefd_);
            idlefd_ = ::accept(listenfd_, NULL, NULL);
            ::close(idlefd_);
            idlefd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
        }
        return;
    }
    assert(connfd > 0);

    if(SetSockoptKeepAlive(connfd) == -1)
    {
        printf("Acceptor::NewConnection SetSockoptKeepAlive failed\n");
        ::close(connfd);
        return;
    }

    if(SetSockoptTcpNoDelay(connfd) == -1)
    {
        printf("Acceptor::NewConnection SetSockoptTcpNoDelay failed\n");
        ::close(connfd);
        return;
    }

    new_connection_callback_(connfd);
}
